#pragma once
#include "../common/net.hpp"
#include "../common/message.hpp"
#include <future>
namespace bitrpc
{
    namespace client
    {
        class Requestor
        //客户端可能发送很多请求，由于muduo库的多线程异步IO特性，可能造成请求与响应无法对应起来，
        //所以将请求id和请求服务描述对象绑定起来作为一组映射关系存到哈希表，这样在收到响应时就可以通过消息id查到请求了；
        //另外IO操作也就是发送请求和接收响应都是其他线程做的，那么可以通过异步关联的方式（promise）将异步结果（响应response）
        //保存在future里，也可以直接用回调方法处理响应

        {
        public:
            using requestCallback = std::function<void(BaseMessage::ptr &)>;//request描述对象的回调方法
            using ptr = std::shared_ptr<Requestor>;
            using AsyncResponse = std::future<BaseMessage::ptr>;
            struct RequestDescribe
            {//请求服务描述对象
                using ptr = std::shared_ptr<RequestDescribe>;
                BaseMessage::ptr request;//真正的请求报文
                RType rtype;//请求类型
                //分为三种：同步调⽤：发起调⽤后，等收到响应结果后返回；
                //异步调⽤：发起调⽤后⽴即返回，在想获取结果的时候进⾏获取
                //回调调⽤：发起调⽤的同时设置结果的处理回调，收到响应后⾃动对结果进⾏回调处理
                std::promise<BaseMessage::ptr> response;//请求报文所对应的异步关联方法，若请求类型是异步的，那就直接将响应填充到promise
                requestCallback callback;//处理响应的回调方法

               
            };
            //该函数会被注册到dispatcher模块，处理响应信息
            void onResponse(const BaseConnection::ptr &conn, BaseMessage::ptr &msg)
            {
                // DLOG("收到响应，开始处理");
                //收到响应信息后，先根据请求id找到对应的请求描述对象
                std::string rid = msg->rid();
                RequestDescribe::ptr req = getDescribe(rid);
                if (req.get() == nullptr)
                {
                    ELOG("收到响应-%s,但未找到对应的请求描述！", rid.c_str());
                    return;
                }
                //再看请求类型，如果是异步，就将响应信息填充给异步关联的promise对象
                if (req->rtype == RType::REQ_ASYNC)
                {
                    // DLOG("异步类型，填充promise对象");
                    req->response.set_value(msg);
                }
                //如果请求类型是回调的，就用回调函数处理响应
                else if (req->rtype == RType::REQ_CALLBACK)
                {
                    if (req->callback)
                        req->callback(msg);
                }
                else
                {
                    ELOG("请求类型未知！");
                }
                delDescribe(rid);
            }
            //发送请求，以异步关联的方式获取响应信息
            bool send(const BaseConnection::ptr &conn, const BaseMessage::ptr &req, AsyncResponse &async_rsp)
            {
                //在发送请求之前，先构造出该请求的描述对象并插入到哈希表维护起来
                RequestDescribe::ptr rdp = newDescribe(req, RType::REQ_ASYNC);
                if (rdp.get() == nullptr)
                {
                    ELOG("构造请求描述对象失败");
                    return false;
                }
                conn->send(req);
                // DLOG("rpc异步请求发送完成……");

                //发送请求后，先把rdp的promise对象response关联过来，一旦收到响应信息，会在onMessage里将相应信息填充进去，
                //在此之前是非阻塞的
                async_rsp = rdp->response.get_future();
                // DLOG("rpc异步结果获取完成……");

                return true;
            }//发送请求，以同步的方式获取响应信息
            bool send(const BaseConnection::ptr &conn,const BaseMessage::ptr &req, BaseMessage::ptr &rsp)
            {
                // DLOG("开始rpc同步发送……");

                AsyncResponse resp_future;
                bool ret = send(conn, req, resp_future);
                if (!ret)
                {
                // ELOG("同步发送失败……");

                    return false;
                }
                // DLOG("rpc同步发送完成……");

               //只需要在异步发送的基础上，用future对象的get()方法进行阻塞即可；
                rsp = resp_future.get();
                // DLOG("rpc同步结果获取完成……");

                return true;
            }
             //发送请求，以回调函数的方式处理响应信息
            bool send(const BaseConnection::ptr &conn, const BaseMessage::ptr &req,  requestCallback cb)
            {
                //还是要先构建描述对象
                RequestDescribe::ptr rdp = newDescribe(req, RType::REQ_CALLBACK,cb);
                if (rdp.get() == nullptr)
                {
                    ELOG("构造请求描述对象失败");
                    return false;
                }
                conn->send(req);
                 //发送请求后，在onMssage模块就会用回调方法处理响应
                return true;
            }

        private:
            RequestDescribe::ptr newDescribe(const BaseMessage::ptr &req, RType rtype,
                                             const requestCallback &callback = requestCallback())
            {//在哈希表中增加一组消息id——请求描述对象的映射关系
                std::unique_lock<std::mutex> lock(_mutex);
                auto req_desc = std::make_shared<RequestDescribe>();
                req_desc->request = req;
                req_desc->rtype = rtype;
                if (req_desc->rtype == RType::REQ_CALLBACK && callback)
                {
                    req_desc->callback = callback;
                }
                _request_desc[req->rid()] = req_desc;
                return req_desc;
            }
            RequestDescribe::ptr getDescribe(const std::string &rid)
            {//查找请求描述对象
                std::unique_lock<std::mutex> lock(_mutex);
                if (_request_desc.count(rid) == 0)
                {
                    return RequestDescribe::ptr();
                }
                return _request_desc[rid];
            }
            void delDescribe(const std::string &rid)
            {//删除请求描述对象
                std::unique_lock<std::mutex> lock(_mutex);
                _request_desc.erase(rid);
            }

        private:
            std::mutex _mutex;
            std::unordered_map<std::string, RequestDescribe::ptr> _request_desc;
        };
    }
}